package org.opencloudb.front.sql;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.SQLWarning;
import java.sql.Statement;

import org.apache.log4j.Logger;
import org.opencloudb.BaseService;
import org.opencloudb.backend.BackendPool;
import org.opencloudb.backend.core.PhysicalDatasource;
import org.opencloudb.front.route.RouteResult;
import org.opencloudb.front.sql.error.SQLError;

public class MycatStatement implements Statement {

	protected Statement realStat;

	protected MycatConnection fakeConn;

	protected int createMethodByCon;

	protected int autoGeneratedKeys = Statement.NO_GENERATED_KEYS;

	protected int[] columnIndexes;

	protected String[] columnNames;

	protected int resultSetConcurrency;

	protected int resultSetHoldability;

	protected int maxFieldSize;

	protected int maxRows;

	protected boolean escapeProcessing = true;

	protected int queryTimeout;

	protected String cursorName;

	protected int fetchDirection;

	protected int fetchSize;

	protected boolean poolable = true;

	protected int resultSetType;

	static final int CREATE_ST_METHOD_BY_CON = 11;
	static final int CREATE_ST_METHOD_BY_CON_I_I = 12;
	static final int CREATE_ST_METHOD_BY_CON_I_I_I = 13;

	public MycatStatement(MycatConnection conn) {
		this.fakeConn = conn;
		this.createMethodByCon = MycatStatement.CREATE_ST_METHOD_BY_CON;
	}

	public MycatStatement(MycatConnection conn, int resultSetType,
			int resultSetConcurrency) {
		this(conn);
		this.resultSetType = resultSetType;
		this.resultSetConcurrency = resultSetConcurrency;
		this.createMethodByCon = MycatStatement.CREATE_ST_METHOD_BY_CON_I_I;
	}

	public MycatStatement(MycatConnection conn, int resultSetType,
			int resultSetConcurrency, int resultSetHoldability) {
		this(conn);
		this.resultSetType = resultSetType;
		this.resultSetConcurrency = resultSetConcurrency;
		this.resultSetHoldability = resultSetHoldability;
		this.createMethodByCon = MycatStatement.CREATE_ST_METHOD_BY_CON_I_I;
	}

	@Override
	public void addBatch(String sql) throws SQLException {
		throw SQLError.notImplemented();
	}

	@Override
	public void clearBatch() throws SQLException {
		throw SQLError.notImplemented();
	}

	@Override
	public int[] executeBatch() throws SQLException {
		throw SQLError.notImplemented();
	}

	@Override
	public boolean isWrapperFor(Class<?> iface) throws SQLException {
		if (realStat == null) {
			throw SQLError.createSQLException("Real Statement don't exist");
		}
		return realStat.isWrapperFor(iface);
	}

	@Override
	public <T> T unwrap(Class<T> iface) throws SQLException {
		if (realStat == null) {
			throw SQLError.createSQLException("Real Statement don't exist");
		}
		return realStat.unwrap(iface);
	}

	@Override
	public void cancel() throws SQLException {
		if (realStat == null) {
			throw SQLError.createSQLException("Real Statement don't exist");
		}
		realStat.cancel();
	}

	@Override
	public void clearWarnings() throws SQLException {
		if (realStat == null) {
			throw new SQLException("Real Statement don't exist");
		}
		realStat.clearWarnings();
	}

	@Override
	public void close() throws SQLException {
		if (realStat != null) {
			realStat.close();
			this.reset();
		}
	}

	@Override
	public void closeOnCompletion() throws SQLException {
		if (realStat == null) {
			throw new SQLException("Real Statement don't exist");
		}
		realStat.closeOnCompletion();
	}

	@Override
	public boolean execute(String sql, int autoGeneratedKeys)
			throws SQLException {
		prepare(sql);
		return realStat.execute(sql, autoGeneratedKeys);
	}

	@Override
	public boolean execute(String sql, int[] columnIndexes) throws SQLException {
		prepare(sql);
		return realStat.execute(sql, columnIndexes);
	}

	@Override
	public boolean execute(String sql, String[] columnNames)
			throws SQLException {
		prepare(sql);
		return realStat.execute(sql, columnNames);
	}

	@Override
	public boolean execute(String sql) throws SQLException {
		prepare(sql);
		return realStat.execute(sql);
	}

	@Override
	public ResultSet executeQuery(String sql) throws SQLException {
		prepare(sql);
		return realStat.executeQuery(sql);
	}

	@Override
	public int executeUpdate(String sql, int autoGeneratedKeys)
			throws SQLException {
		prepare(sql);
		return realStat.executeUpdate(sql, autoGeneratedKeys);
	}

	@Override
	public int executeUpdate(String sql, int[] columnIndexes)
			throws SQLException {
		prepare(sql);
		return realStat.executeUpdate(sql, columnIndexes);
	}

	@Override
	public int executeUpdate(String sql, String[] columnNames)
			throws SQLException {
		prepare(sql);
		return realStat.executeUpdate(sql, columnNames);
	}

	@Override
	public int executeUpdate(String sql) throws SQLException {
		prepare(sql);
		return realStat.executeUpdate(sql);
	}

	@Override
	public Connection getConnection() throws SQLException {
		Connection realCon = fakeConn.getRealConn();
		if (realCon != null) {
			return realCon;
		} else {
			throw new SQLException("real Connection don't exist");
		}
	}

	@Override
	public ResultSet getGeneratedKeys() throws SQLException {
		if (realStat == null) {
			throw new SQLException("real Statement don't exist");
		}
		return realStat.getGeneratedKeys();
	}

	@Override
	public boolean getMoreResults() throws SQLException {
		if (realStat == null) {
			throw new SQLException("real Statement don't exist");
		}
		return realStat.getMoreResults();
	}

	@Override
	public boolean getMoreResults(int current) throws SQLException {
		if (realStat == null) {
			throw new SQLException("real Statement don't exist");
		}
		return realStat.getMoreResults(current);
	}

	@Override
	public ResultSet getResultSet() throws SQLException {
		if (realStat == null) {
			throw new SQLException("real Statement don't exist");
		}
		return realStat.getResultSet();
	}

	@Override
	public int getResultSetConcurrency() throws SQLException {
		if (realStat == null) {
			throw new SQLException("real Statement don't exist");
		}
		return realStat.getResultSetConcurrency();
	}

	@Override
	public int getUpdateCount() throws SQLException {
		if (realStat == null) {
			throw new SQLException("real Statement don't exist");
		}
		return realStat.getUpdateCount();
	}

	@Override
	public SQLWarning getWarnings() throws SQLException {
		if (realStat == null) {
			throw new SQLException("real Statement don't exist");
		}
		return realStat.getWarnings();
	}

	@Override
	public boolean isCloseOnCompletion() throws SQLException {
		if (realStat == null) {
			throw new SQLException("real Statement don't exist");
		}
		return realStat.isCloseOnCompletion();
	}

	@Override
	public boolean isClosed() throws SQLException {
		if (realStat == null) {
			throw new SQLException("real Statement don't exist");
		}
		return realStat.isClosed();
	}

	@Override
	public int getResultSetType() throws SQLException {
		if (realStat == null) {
			throw new SQLException("real Statement don't exist");
		}
		return realStat.getResultSetType();
	}

	/*
	 * TIP 属性相关的get、set
	 */

	@Override
	public void setCursorName(String name) throws SQLException {
		if (realStat != null) {
			realStat.setCursorName(name);
		}
		this.cursorName = name;
	}

	@Override
	public int getResultSetHoldability() throws SQLException {
		if (realStat != null) {
			return realStat.getResultSetHoldability();
		}
		return this.resultSetHoldability;
	}

	@Override
	public void setEscapeProcessing(boolean enable) throws SQLException {
		if (realStat != null) {
			realStat.setEscapeProcessing(enable);
		}
		this.escapeProcessing = enable;
	}

	@Override
	public void setFetchDirection(int direction) throws SQLException {
		if (realStat != null) {
			realStat.setFetchDirection(direction);
		}
		this.fetchDirection = direction;
	}

	@Override
	public void setFetchSize(int rows) throws SQLException {
		if (realStat != null) {
			realStat.setFetchSize(rows);
		}
		this.fetchSize = rows;
	}

	@Override
	public void setMaxFieldSize(int max) throws SQLException {
		if (realStat != null) {
			realStat.setMaxFieldSize(max);
		}
		this.maxFieldSize = max;
	}

	@Override
	public void setMaxRows(int max) throws SQLException {
		if (realStat != null) {
			realStat.setMaxRows(max);
		}
		this.maxRows = max;
	}

	@Override
	public void setPoolable(boolean poolable) throws SQLException {
		if (realStat != null) {
			realStat.setPoolable(poolable);
		}
		this.poolable = poolable;
	}

	@Override
	public void setQueryTimeout(int seconds) throws SQLException {
		if (realStat != null) {
			realStat.setQueryTimeout(seconds);
		}
		this.queryTimeout = seconds;
	}

	@Override
	public boolean isPoolable() throws SQLException {
		if (realStat != null) {
			return realStat.isPoolable();
		}
		return this.poolable;
	}

	@Override
	public int getQueryTimeout() throws SQLException {
		if (realStat != null) {
			return realStat.getQueryTimeout();
		}
		return this.queryTimeout;
	}

	@Override
	public int getMaxFieldSize() throws SQLException {
		if (realStat != null) {
			return realStat.getMaxFieldSize();
		}
		return this.maxFieldSize;
	}

	@Override
	public int getMaxRows() throws SQLException {
		if (realStat != null) {
			return realStat.getMaxRows();
		}
		return this.maxRows;
	}

	@Override
	public int getFetchDirection() throws SQLException {
		if (realStat != null) {
			return realStat.getFetchDirection();
		}
		return this.fetchDirection;
	}

	@Override
	public int getFetchSize() throws SQLException {
		if (realStat != null) {
			return realStat.getFetchSize();
		}
		return this.fetchSize;
	}

	/*
	 * TIP 自定方法
	 */

	protected void reset() {
		this.realStat = null;
		this.fakeConn = null;
		this.autoGeneratedKeys = Statement.NO_GENERATED_KEYS;
		this.columnIndexes = null;
		this.columnNames = null;
		this.resultSetType = 0;
		this.resultSetConcurrency = 0;
		this.resultSetHoldability = 0;
		this.maxFieldSize = 0;
		this.maxRows = 0;
		this.escapeProcessing = true;
		this.queryTimeout = 0;
		this.cursorName = null;
		this.fetchDirection = 0;
		this.fetchSize = 0;
		this.poolable = true;
	}

	/**
	 * 初始化真正的Statement
	 * 
	 * @param sql
	 *            已经初始化好的sql语句。
	 * @throws SQLException
	 */
	protected void prepare(String sql) throws SQLException {

		RouteResult rrs = BaseService.getInstance().getRouteService()
				.route(sql);

		PhysicalDatasource physicalDs = BackendPool.getInstance().getDataSouce(
				rrs.getTartgetHost());

		if (physicalDs == null) {
			physicalDs = BackendPool.getInstance().getAlivePhysicalDatasource();
		}

		fakeConn.setRealConn(physicalDs.getConnection());

		if (fakeConn.getRealConn() == null) {
			throw new SQLException("连接没有正确获得");
		}
		// 创建Statement
		createStatements(sql);

	}

	protected void createStatements(String sql) throws SQLException {
		switch (this.createMethodByCon) {
		case CREATE_ST_METHOD_BY_CON:
			realStat = fakeConn.getRealConn().createStatement();
			break;
		case CREATE_ST_METHOD_BY_CON_I_I:
			realStat = fakeConn.getRealConn().createStatement(resultSetType,
					resultSetConcurrency);
			break;
		case CREATE_ST_METHOD_BY_CON_I_I_I:
			realStat = fakeConn.getRealConn().createStatement(resultSetType,
					resultSetConcurrency, resultSetHoldability);
			break;
		}
		if (realStat == null) {
			throw new SQLException("无法建立正确的ps");
		}
		if (this.maxFieldSize != 0) {
			realStat.setMaxFieldSize(maxFieldSize);
		}
		if (this.maxRows != 0) {
			realStat.setMaxRows(maxRows);
		}
		if (!this.escapeProcessing) {
			realStat.setEscapeProcessing(escapeProcessing);
		}
		if (this.queryTimeout != 0) {
			realStat.setQueryTimeout(queryTimeout);
		}
		if (this.cursorName != null) {
			realStat.setCursorName(cursorName);
		}
		if (this.fetchDirection != 0) {
			realStat.setFetchDirection(fetchDirection);
		}
		if (this.fetchSize != 0) {
			realStat.setFetchSize(fetchSize);
		}
		if (!this.poolable) {
			realStat.setPoolable(poolable);
		}
	}

}
